/*
 * 智能浇水模块
{
  "method": "set",
  "watering": "ON"/"OFF"
}

{
  "method": "get",
  "obj": ["watering"]
}
 */

#include "Arduino.h"    
#include <OneButton.h>       
#include <WiFi.h>
#include <sddc.h>
#include <cJSON.h>
#include <Wire.h>
#include <SDDC_SDK_lib.h>


#define SDDC_CFG_PORT         680U                 // SDDC 协议使用的端口号
#define PIN_INPUT 0                                // 选择 IO0 进行控制
#define ESP_TASK_STACK_SIZE   4096
#define ESP_TASK_PRIO         25 
#define MESSAGE_Q_NUM         5                    // 数据的消息队列的数量 
#define MESSAGE_REC_LEN       5                    // 数据的消息队列的长度

static sddc_t *g_sddc;
static const char* ssid     = "EOS-Tenda";         // WiFi 名
static const char* password = "1234567890";        // WiFi 密码

static const int water_pin    = 12;                // 浇水器的控制引脚，控制浇水器启停
static const int sign_pin     = A0;                // 浇水器的状态监视引脚，查看浇水器是否休眠
static const int monitor_pin  = 5;                 // 工作状态监视引脚，监视浇水器启停

QueueHandle_t Message_Queue;

static  int xTicksToDelay = 5000;                  // 周期延时时间

OneButton button(PIN_INPUT, true);

/* 
 *  主动数据上报函数
 */
static void report_sensor_state()
{  
    int sensorValue = 0;
    cJSON *value;
    cJSON *root;
     
    value =  cJSON_CreateArray();
    root = cJSON_CreateObject();
    sddc_return_if_fail(value);
    sddc_return_if_fail(root);
            
    // 按格式生成需要的数据
    cJSON_AddItemToArray(value, cJSON_CreateString("watering"));    // 这里的字符串要和系统对象状态获取注册结构体里的对应
    // cJSON_AddItemToArray(value, cJSON_CreateString("上报数据 2 ")); // 需要上报几个就添加几个  
    cJSON_AddItemToObject(root, "obj", value);
      
    // 发送数据给 EdgerOS
    object_report(root);
      
    cJSON_Delete(value);
}

/*
 * 浇水状态监控函数
 */
static void monitor_task(void *arg)
{
    int newval = 1;
    int oldval = 1;

    // 监控浇水开启和关闭状态
    while(1)
    {
        newval = digitalRead(monitor_pin);
        if(newval != oldval)
        {
            report_sensor_state();
        } 
        oldval = newval;
        // 任务创建之后，设定延时周期
        delay(100);
     }
     vTaskDelete(NULL);
}

/*
 * 浇水触发任务
 */
static void button_task(void *arg)
{
    char SW[5];
    char *value;
    BaseType_t err;                               
    
    if(Message_Queue != NULL)                             
    {               
        err = xQueueReceive(Message_Queue, &value, portMAX_DELAY );              
        if(err == pdFALSE)                           
        {                           
            sddc_printf("队列 Message_Queue 数据获取失败!\r\n");                        
        }                           
    }

    if(digitalRead(monitor_pin))
    {
        strcpy(SW,"OFF");
    }else
    {
        strcpy(SW,"ON");
    }
    // 开关
    if(0 != strcmp(value,SW) && (value != NULL))
    {
        // 判断机器是否休眠如果休眠了就行唤醒机器
        delay(100);
        int a = analogRead(sign_pin);
        sddc_printf("\n 休眠状态 == : %d！！！！！\r\n", analogRead(sign_pin));

        if(!(a > 1) && (0 == strcmp(value,"ON")))
        {
            Serial.println("唤醒");
            digitalWrite(water_pin, HIGH);
            delay(100);
            digitalWrite(water_pin, LOW);
            delay(100);
        }
      
        Serial.println("触发");
        digitalWrite(water_pin, HIGH);
        delay(100);
        digitalWrite(water_pin, LOW);
        delay(100);
    }
    vTaskDelete(NULL);
}

/*
 * 浇水器控制函数
 */
sddc_bool_t water_set(const char* value)
{
    BaseType_t err;                               
      
    sddc_printf("\niot_pi_on_message: %s！！！！！\n", value);

    if((Message_Queue != NULL)&&(value))                              
    {                             
        err = xQueueSendToFront(Message_Queue,&value,0 );                            
        if(err == pdFALSE)                       
        {                           
            sddc_printf("队列 Message_Queue 已满，数据发送失败!\r\n");                        
        }                           
    }

    // 创建电机触发任务，防止阻塞message_ack回复
    xTaskCreate(button_task, "button_task", ESP_TASK_STACK_SIZE, NULL, ESP_TASK_PRIO, NULL);
    return SDDC_TRUE;
}

/* 
 *  填写浇水状态
 */
sddc_bool_t single_get_sensor(char *objvalue, int value_len)
{
    if(digitalRead(monitor_pin))
    {
        strncpy(objvalue, "OFF", value_len);
    }else
    {
        strncpy(objvalue, "ON", value_len);
    }
        return SDDC_TRUE;
}

/* 
 *  数字量设备对象函数与处理方法注册
 */
NUM_DEV_REGINFO num_dev[] = {
};

/*
 *  显示设备对象函数与处理方法注册
 */
DIS_DEV_REGINFO dis_dev[] = {
};

/*
 * IO设备对象设置函数与处理方法注册
 */
IO_DEV_REGINFO io_dev[] = {
        {"watering",water_set},
};

/*
 *  系统对象状态获取注册
 */
DEV_STATE_GET  dev_state_get_reg[] = {
        {"watering",   DEV_IO_TYPE,  single_get_sensor},
};

/*
 *  当前设备的信息定义
 */
DEV_INFO    dev_info = {
            .name     = "智能浇水器",
            .type     = "device",
            .excl     = SDDC_FALSE,
            .desc     = "ESP-32S",
            .model    = "1",
            .vendor   = "inspiration-desktop",
};

/*
 *   系统注册对象汇聚
 */
SDDC_CONFIG_INFO sys_cfg = {
        .token             = "1234567890",            // 设备密码
        .devinfo           = &dev_info,               
        .io_dev_reg        = io_dev,
        .io_dev_reg_num    = ARRAY_SIZE(io_dev),
        .num_dev_reg       = num_dev,
        .num_dev_reg_num   = ARRAY_SIZE(num_dev),
        .state_get_reg     = dev_state_get_reg,
        .state_get_reg_num = ARRAY_SIZE(dev_state_get_reg),
        .dis_dev_reg       = dis_dev,
        .dis_dev_num       = ARRAY_SIZE(dis_dev),
};


/*
 * IO0 按键检测任务
 */
static void esp_io0_key_task()
{
    WiFi.disconnect();
    while (WiFi.status() != WL_CONNECTED)
    {
        delay(1000);
        Serial.println("WIFI Wait for Smartconfig");
        
        // 设置为Station模式
        WiFi.mode(WIFI_STA);
        
        // 开始智能配网
        WiFi.beginSmartConfig();
        while (1)
        {
            delay(500);
            // 检查智能配网
            if (WiFi.smartConfigDone())
            {
                // 设置自动连接
                WiFi.setAutoConnect(true);  
                break;
            }
        }
    }
}
/*
 * 循环扫描按键
 */
static void esp_tick_task(void *arg)
{
    void *sddc = arg;
    (void)sddc;
    while(1)
    {
        button.tick();
        delay(100);
    }
}

/*
 * 初始化传感器模块
 */
void sensor_init()
{
    pinMode(water_pin, OUTPUT);
    pinMode(sign_pin, INPUT);
    pinMode(monitor_pin,INPUT);

    Message_Queue = xQueueCreate(MESSAGE_Q_NUM, MESSAGE_REC_LEN);                 //创建消息Message_Queue      
    if(Message_Queue == 0)
    {
        printf("队列 Message_Queue 创建失败!\r\n");                        
    }
}

void setup() {
    byte mac[6];
    Serial.begin(115200);
    Serial.setDebugOutput(true);
    Serial.println();

    // 初始化传感器
    sensor_init();

    // 清除一下按键状态机的状态
    button.reset();
  
    // 创建按键扫描线程，长按 IO0 按键，松开后ESP32 将会进入 SmartConfig 模式
    sddc_printf("长按按键进入 Smartconfig...\n");
    button.attachLongPressStop(esp_io0_key_task);
    xTaskCreate(esp_tick_task, "button_tick", ESP_TASK_STACK_SIZE, NULL, ESP_TASK_PRIO, NULL);
    
    // 启动 WiFi 并且连接网络
    WiFi.begin(ssid, password);
    while (WiFi.status() != WL_CONNECTED) 
    {
        delay(500);
        Serial.print(".");
    }
  
    // 获取并打印 IP 地址
    Serial.println("");
    Serial.println("WiFi connected");
    Serial.print("'ip :");
    Serial.print(WiFi.localIP());
    Serial.println("' to connect"); 
  
    // 创建监视浇水线程
    xTaskCreate(monitor_task, "monitor_task", ESP_TASK_STACK_SIZE, NULL, ESP_TASK_PRIO, NULL);

    // sddc协议初始化
    sddc_lib_main(&sys_cfg);

    // 获取并打印网卡 mac 地址
    WiFi.macAddress(mac);
    sddc_printf("MAC addr: %02x:%02x:%02x:%02x:%02x:%02x\n",
              mac[5], mac[4], mac[3], mac[2], mac[1], mac[0]);
    // 使用网卡 mac 地址设置设备唯一标识 UID
    sddc_set_uid(G_sddc, mac);
}

void loop() {
    // 运行 SDDC 协议循环
    while (1) 
    {
        sddc_printf("SDDC running...\n");
        sddc_run(G_sddc);
        sddc_printf("SDDC quit!\n");
    }

    // 销毁 SDDC 协议
    sddc_destroy(G_sddc);
}
